优化加载性能:了解异步上传管线AUP
没有人喜欢看到加载界面,你是否知道可以快速调整Async Upload Pipeline异步上传管线(AUP)的参数来有效提高加载速度。
本文将详细介绍如何通过AUP加载网格和纹理,这将有效提升加载速度,甚至能实现超过2倍的性能提升效果。我们将从技术角度介绍AUP的工作原理,以及应该使用哪些API来充分利用AUP。
Asset Upload Pipeline资源上传管线的最新优化实现在Unity 2018.3 beta中提供。让我们首先了解AUP应该在什么时候使用以及加载过程的工作原理。
何时使用Async Upload Pipeline
Unity 2018.3之前,AUP仅处理纹理。从Unity 2018.3 beta开始,AUP可以加载纹理和网格,但有部分例外情况,可读写纹理、可读写网格和压缩网格都不使用AUP。
请注意:Unity 2018.2中的纹理Mipmap流也使用AUP。
加载过程如何工作
在构建过程中,纹理或网格对象会写入序列化文件,大型二进制数据的纹理或顶点数据会写入附带的.resS文件,这样的配置应用于玩家数据和资源包。
分离对象和二进制数据能更快地加载序列化文件,通常该文件包含多个小型对象,而且此后还能从.resS文件流式加载大型二进制数据。
当纹理或网格对象反序列化后,它会向AUP的指令队列提交一个指令。当该指令完成时,纹理或网格数据会上传到GPU,于是该对象可以被集成到主线程。
网格和纹理数据为构建进行序列化时的配置
在上传过程中,.resS文件中的大型二进制数据会被读取到固定大小的环形缓冲区中。当数据进入内存后,数据会在渲染线程上以时间切片的方式上传到GPU。环形缓冲区的大小和时间切片的持续时间这二个参数可通过修改来影响系统行为。
AUP对每个指令会执行以下过程:
等待环形缓冲区中所需内存可用。
从源.resS文件中读取数据到分配的内存。
执行后期处理过程,例如:纹理解压、网格碰撞生成、每个平台的修复等。
以时间切片的方式在渲染线程进行上传。
释放环形缓冲区内存。
多个指令可以同时进行,但是所有指令必须在共享的环形缓冲区中分配所需的内存。当环形缓冲区被填满时,新指令会等待,这种等待不会造成主线程阻塞或影响帧率,它只会减慢异步加载过程。
这些影响总结如下:
没有AUP
AUP
影响
内存使用
从默认堆读取数据时分配内存
固定大小的环形缓冲区
减小高内存标记
上传过程
在数据可用时上传
使用固定时间切片进行分期上传
无障碍上传
后期处理
在加载线程执行,该过程会阻碍加载线程
在后台的作业执行
更快的加载速度
可调整加载参数的公共API
为了充分使用Unity 2018.3的AUP,以下是三个可以在运行时调整该系统的参数。
1
QualitySettings.asyncUploadTimeSlice
该参数设定渲染线程中每帧上传纹理和网格数据所用的时间总量,以毫秒为单位。当异步加载操作进行时,该系统会执行二个该参数大小的时间切片,该参数的默认值为2毫秒。
如果该值太小,可能会在纹理/网格的GPU上传时遇到瓶颈。而该值太大的话,会造成帧率陡降。
2
QualitySettings.asyncUploadBufferSize
该参数设定环形缓冲区的大小,以MB为单位。当上传时间切片在每帧发生时,要确保在环形缓冲区有足够的数据利用整个时间切片。
如果环形缓冲区太小,上传时间切片会被缩短。Unity 2018.2的环形缓冲区默认大小为4MB,Unity 2018.3中提高至16MB。
3
QualitySettings.asyncUploadPersistentBuffer
该标识在Unity 2018.3加入,它决定在完成所有待定读取工作时,是否释放上传时使用的环形缓冲区。
分配和释放该缓冲区经常会产生内存碎片,因此通常将其保留为默认值True。如果需要在未加载时回收内存,可以将该值设为False。
这些设置可以通过脚本API或QualitySettings菜单调整。
示例工作流程
下面使用默认2ms时间切片和4MB环形缓冲区,查看通过AUP上传大量纹理和网格时的工作负载。
由于是加载过程,每个渲染帧有2个时间切片,所以应得到4毫秒的上传时间。性能分析器的数据表示,我们只使用了大约1.5毫秒。
在上传完成后,AUP会立即发出新的读取操作,因为环形缓冲区中有了可用内存。这表示我们需要更大的环形缓冲区。
现在尝试增加环形缓冲区的大小,由于正处于加载界面,提高上传的时间切片也是个不错的方法。
下面是使用16MB环形缓冲区和4毫秒时间切片的效果。
现在可以看到,我们几乎将所有渲染线程时间用于上传,只在每次上传之间的短暂时间渲染帧。
下面是以不同上传时间切片和环形缓冲区大小处理示例工作量时所用的加载时间。
该测试运行于MacBook Pro,CPU为2.8GHz Intel Core i7,系统是OS X El Capitan。上传速度和I/O速度会因不同平台和设备而异。该工作量是我们在内部用于性能测试的Viking Village示例项目的一个子集。
因为还加载着其它对象,所以我们无法获得不同数值的准确性能优势。但是,在这种情况下,从4MB/2MS的设置改为16MB/4MS的设置后,加载纹理和网格的速度变成了原来的2倍。
使用这些参数进行测试得到以下结果。
为了优化示例项目的加载时间,我们应该使用以下设置。
QualitySettings.asyncUploadTimeSlice = 4
QualitySettings.asyncUploadBufferSize = 16
QualitySettings.asyncUploadPersistentBuffer = true
建议
优化纹理和网格加载速度的常用建议如下:
选择不会导致掉帧的最大QualitySettings.asyncUploadTimeSlice。
在加载界面时,可以临时提高QualitySettings.asyncUploadTimeSlice。
使用性能分析器来检查时间切片的利用率。时间切片在性能分析器中会显示为AsyncUploadManager.AsyncResourceUpload。如果时间切片没有完全利用的话,就提高QualitySettings.asyncUploadBufferSize。
使用更大的QualitySettings.asyncUploadBufferSize会提高加载速度,所以如果内存足够的话,请将其从16MB提高至32MB。
将QualitySettings.asyncUploadPersistentBuffer保留为true,除非有理由在未加载时减少运行时内存的使用。
FAQ
问题1:在渲染线程上,发生时间切片上传的频率是多少?
回答:时间切片上传在每个渲染帧发生一次,在异步加载操作时发生二次。VSync会影响该管线。当渲染线程等待VSync时,可以进行上传。
如果以16ms的帧运行,且其中一帧延长的话,例如变为17ms,就需要等待15ms的VSync。通常情况下,帧率越高,上传时间切片的发生频率越高。
问题2:什么内容可以通过AUP上传?
回答:
不可读写的纹理会通过AUP上传。
Unity 2018.2中,纹理Mipmap会通过AUP流式上传。
Unity 2018.3中,只要网格未压缩而且未启用读取或写入,网格也会通过AUP上传。
问题3:如果环形缓冲区的大小不足以保存上传的数据,例如非常大的纹理,会发生什么?
回答:上传指令大于环形缓冲区时,会等待环形缓冲区完全耗尽,然后重新分配环形缓冲区以适应较大的分配量。当上传完成后,环形缓冲区会重新分配为原始大小。
问题4:同步加载API是如何工作的?例如:Resources.Load,AssetBundle.LoadAsset等。
回答:同步加载调用使用AUP,它会阻挡主线程,直到异步上传操作完成。所使用的加载API类型并不重要。
小结
希望大家利用异步上传管线AUP加速你的游戏加载速度,更多Unity最新功能介绍尽在Unity官方中文论坛(UnityChina.cm)!
推荐阅读
官方活动
Unity将在10月22-26日,举办为期5天的专业的Unity官方教师培训课程,诚邀广大教师与Unity一同学习分享最新技术![了解详情...]
报名地址:
https://connect.unity.com/events/2018jiaoshipeixun
现在访问Unity在线商店(store.unity.com),成功订阅Unity Pro专业版、Unity Plus加强版即可享受全新增值服务组合。11月18日之前订阅,更有指定插件资源限时赠送。[了解详情...]
活动地址:https://store.unity.com/cn
点击“阅读原文”访问Unity官方中文论坛